package com.wao.file.service;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Recorder;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.persistent.FileRecorder;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.wao.file.domain.BatchStatus;
import com.wao.file.domain.FileInfo;
import com.wao.file.domain.FileKey;
import com.wao.file.domain.FileListing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;

@Component
public class QiniuFileStorage extends AbstractFileStorage {

    private final Logger log = LoggerFactory.getLogger(QiniuFileStorage.class);


    @Value("${wao.file.qiniu.access-key:}")
    private String accessKey;

    @Value("${wao.file.qiniu.secret-key:}")
    private String secretKey;

    @Value("${wao.file.qiniu.record-path:record}")
    private String recordPath;

    private List<String> domains = new ArrayList<>();

    private Auth auth;

    private Configuration conf;

    private BucketManager bucketManager;

    private UploadManager uploadManager;

    public String getAccessKey() {
        return accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    @PostConstruct
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        this.auth = Auth.create(this.accessKey, this.secretKey);
        Zone z = Zone.autoZone();
        this.conf = new Configuration(z);
        this.bucketManager = new BucketManager(this.auth, conf);
        this.uploadManager = new UploadManager(conf);
    }

    public List<String> getDomains() throws QiniuException {
        if (this.domains.isEmpty()) {
            String[] domainList = bucketManager.domainList(super.getBucket());
            this.domains = Arrays.asList(domainList);
        }
        return this.domains;
    }

    public String uploadToken() {
        return uploadToken(bucket, null, 3600, null);
    }

    public String uploadToken(String key) {
        return uploadToken(bucket, key, 3600, null);
    }

    @Override
    public String uploadToken(String bucket, String key, long expires, Map<String, Object> policy) {
        return auth.uploadToken(bucket, key, expires, policy == null ? null : new StringMap(policy));
    }

    public FileKey upload(InputStream stream, String key) throws IOException {
        return upload(stream, key, uploadToken(key), null, null);
    }

    public FileKey upload(InputStream stream, String key, String token) throws IOException {
        return upload(stream, key, token, null, null);
    }

    @Override
    public FileKey upload(InputStream stream, String key, String token, Map<String, Object> params, String mime) throws IOException {
        try {
            //调用put方法上传
            Response res = uploadManager.put(stream, key, uploadToken(key), params == null ? null : new StringMap(params), mime);
            return new Gson().fromJson(res.bodyString(), FileKey.class);
        } catch (QiniuException e) {
            Response r = e.response;
            // 请求失败时打印的异常的信息
            String msg = r.toString();
            try {
                //响应的文本信息
                msg = r.bodyString();
            } catch (QiniuException e1) {
                //ignore
            }
            log.error(msg);
            throw new IOException(msg);
        }
    }

    @Override
    public FileKey recordUpload(InputStream stream, String key, String token, Map<String, Object> params, String mime) throws IOException {
        //实例化recorder对象
        Recorder recorder = new FileRecorder(recordPath);
        //实例化上传对象，并且传入一个recorder对象
        UploadManager uploadManager = new UploadManager(conf, recorder);
        try {
            //调用put方法上传
            Response res = uploadManager.put(stream, key, uploadToken(key), params == null ? null : new StringMap(params), mime);
            return new Gson().fromJson(res.bodyString(), FileKey.class);
        } catch (QiniuException e) {
            Response r = e.response;
            // 请求失败时打印的异常的信息
            String msg = r.toString();
            try {
                //响应的文本信息
                msg = r.bodyString();
            } catch (QiniuException e1) {
                //ignore
            }
            log.error(msg);
            throw new IOException(msg);
        }
    }

    public String privateDownload(String fileName) throws IOException {
        return privateDownload(fileName, 3600);
    }

    @Override
    public String privateDownload(String fileName, long expires) throws IOException {
        String domain = getDomains().stream().findAny().get();
        String encodedFileName = URLEncoder.encode(fileName, "utf-8");
        String url = String.format("%s/%s", domain, encodedFileName);
        return auth.privateDownloadUrl(url, expires);
    }

    public FileInfo stat(String fileKey) throws IOException {
        return stat(bucket, fileKey);
    }

    @Override
    public FileInfo stat(String bucket, String fileKey) throws IOException {
        com.qiniu.storage.model.FileInfo fileInfo = bucketManager.stat(bucket, fileKey);
        return new FileInfo(fileInfo);
    }

    public void copy(String fromFileKey, String toFileKey, boolean force) throws IOException {
        copy(bucket, fromFileKey, bucket, toFileKey, force);
    }

    @Override
    public void copy(String fromBucket, String fromFileKey, String toBucket, String toFileKey, boolean force) throws IOException {
        try {
            //调用copy方法移动文件
            bucketManager.copy(fromBucket, fromFileKey, toBucket, toFileKey, force);
        } catch (QiniuException e) {
            //捕获异常信息
            Response r = e.response;
            log.error(r.toString());
            throw new IOException(r.toString());
        }
    }

    public void move(String fromFileKey, String toFileKey, boolean force) throws IOException {
        move(bucket, fromFileKey, bucket, toFileKey, force);
    }

    @Override
    public void move(String fromBucket, String fromFileKey, String toBucket, String toFileKey, boolean force) throws IOException {
        try {
            //调用move方法移动文件
            bucketManager.move(fromBucket, fromFileKey, toBucket, toFileKey, force);
        } catch (QiniuException e) {
            //捕获异常信息
            Response r = e.response;
            log.error(r.toString());
            throw new IOException(r.toString());
        }
    }

    public void delete(String key) throws IOException {
        delete(bucket, key);
    }

    @Override
    public void delete(String bucket, String key) throws IOException {
        try {
            //调用delete方法移动文件
            bucketManager.delete(bucket, key);
        } catch (QiniuException e) {
            //捕获异常信息
            Response r = e.response;
            log.error(r.toString());
            throw new IOException(r.toString());

        }
    }

    public FileListing listFiles(String marker, int limit) throws IOException {
        return listFiles(bucket, null, marker, limit, "/");
    }

    public FileListing listFiles(String prefix, String marker, int limit) throws IOException {
        return listFiles(bucket, prefix, marker, limit, "/");
    }

    public FileListing listFiles(String prefix, String marker, int limit, String delimiter) throws IOException {
        return listFiles(bucket, prefix, marker, limit, delimiter);
    }

    @Override
    public FileListing listFiles(String bucket, String prefix, String marker, int limit, String delimiter) throws IOException {
        try {
            //调用listFiles方法列举指定空间的指定文件
            //参数一：bucket    空间名
            //参数二：prefix    文件名前缀
            //参数三：marker    上一次获取文件列表时返回的 marker
            //参数四：limit     每次迭代的长度限制，最大1000，推荐值 100
            //参数五：delimiter 指定目录分隔符，列出所有公共前缀（模拟列出目录效果）。缺省值为空字符串
            if (limit > 1000)
                limit = 1000;
            com.qiniu.storage.model.FileListing fileListing = bucketManager.listFiles(bucket, prefix, marker, limit, delimiter);
            return new FileListing(fileListing);
        } catch (QiniuException e) {
            //捕获异常信息
            Response r = e.response;
            log.error(r.toString());
            throw new IOException(r.toString());
        }
    }

    public List<BatchStatus> batchDelete(String... keys) throws IOException {
        return batchDelete(bucket, keys);
    }

    @Override
    public List<BatchStatus> batchDelete(String bucket, String... keys) throws IOException {
        try {
            BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
            //调用批量操作的batch方法
            Response res = bucketManager.batch(batchOperations.addDeleteOp(bucket, keys));
            BatchStatus[] batchStatusArray = res.jsonToObject(BatchStatus[].class);
            if (batchStatusArray == null || batchStatusArray.length <= 0)
                return Collections.emptyList();
            return Arrays.asList(batchStatusArray);

        } catch (QiniuException e) {
            //捕获异常信息
            Response r = e.response;
            log.error(r.toString());
            throw new IOException(r.toString());
        }
    }
}
